home *** CD-ROM | disk | FTP | other *** search
/ Gigarom 1 / Gigarom Macintosh Archives (Quantum Leap)(CDRM1080320)(1993).iso / FILES / HYP / C-D / DartmouthXCMDs.cpt / Dartmouth XCMDs Vol 1&2 / card_4530.txt < prev    next >
Text File  |  1989-02-26  |  31KB  |  937 lines

  1. -- card: 4530 from stack: in
  2. -- bmap block id: 0
  3. -- flags: 0000
  4. -- background id: 3241
  5. -- name: PrintField
  6.  
  7.  
  8. -- part 1 (button)
  9. -- low flags: 00
  10. -- high flags: A003
  11. -- rect: left=52 top=300 right=322 bottom=221
  12. -- title width / last selected line: 0
  13. -- icon id / first selected line: 0 / 0
  14. -- text alignment: 1
  15. -- font id: 0
  16. -- text size: 12
  17. -- style flags: 0
  18. -- line height: 16
  19. -- part name: Print the Documentation
  20. ----- HyperTalk script -----
  21. on mouseUp
  22.   printfield "field 1",0
  23. end mouseUp
  24.  
  25.  
  26.  
  27. -- part 12 (field)
  28. -- low flags: 80
  29. -- high flags: 2007
  30. -- rect: left=12 top=26 right=298 bottom=491
  31. -- title width / last selected line: 0
  32. -- icon id / first selected line: 0 / 0
  33. -- text alignment: 0
  34. -- font id: 22
  35. -- text size: 10
  36. -- style flags: 0
  37. -- line height: 13
  38. -- part name: Source
  39.  
  40.  
  41. -- part 13 (button)
  42. -- low flags: 00
  43. -- high flags: A003
  44. -- rect: left=314 top=300 right=322 bottom=435
  45. -- title width / last selected line: 0
  46. -- icon id / first selected line: 0 / 0
  47. -- text alignment: 1
  48. -- font id: 0
  49. -- text size: 12
  50. -- style flags: 0
  51. -- line height: 16
  52. -- part name: Show LSP Source
  53. ----- HyperTalk script -----
  54. on mouseUp
  55.   set the visible of card field 1 to not the visible of card field 1
  56.   if the visible of card field 1 is true then
  57.     set the name of me to "Hide LSP Source"
  58.   else set the name of me to "Show LSP Source"
  59. end mouseUp
  60.  
  61.  
  62.  
  63. -- part contents for background part 16
  64. ----- text -----
  65. PRINTFIELD XCMD version 1.4.1
  66. Kevin Calhoun
  67.  
  68. NOTE TO USERS OF EARLIER VERSIONS:  The parameter list has changed in PrintField 1.4.  Please be sure to read "NEW IN VERSION 1.4" if you would like to replace earlier versions with version 1.4 in your stacks.
  69.  
  70. The PrintField XCMD allows you to print the text of a field in the field's current textFont, textSize, textStyle, textHeight, and width.  You may supply additional information about margins in order to place the text anywhere you like on the page.
  71.  
  72. PrintField uses a dialog box to inform the user that printing is in progress.  It passes to the printer driver the short name of the field to be printed as the document name.
  73.  
  74. In case of an error, PrintField returns an error message as the Result.  Word 1 of this message will be "Error."
  75.  
  76. PrintField can print only one field at a time.  It works with both the LaserWriter and the ImageWriter.
  77.  
  78. NEW IN VERSION 1.4:
  79. The parameter list has changed in order to accommodate users who wanted the option of skipping the Page Setup and Print Job dialog boxes.  A new parameter has been added after the field designation and before the margin settings, called dialogCount, which determines which of the print dialog boxes the user sees.  If you used margin settings with earlier versions of PrintField, you will have to alter your scripts to accord with the new parameter list used by version 1.4.
  80.  
  81. INVOKING PRINTFIELD
  82.  
  83. PrintField "fieldDesignation",<dialogCount>,<leftMargin>,<rightMargin>,<topMargin>,    <bottomMargin>
  84.  
  85. Parameters given inside the brackets, such as  <notNeeded>,  are optional.
  86.  
  87. fieldDesignation:
  88. You may designate the field to print in any way considered valid by HyperCard, by number, id, or name, with one exception:  you can't use the field's name if it is more than one word.  If you do use the field's name, don't put the field name in quotation marks.  Nested quotations confuse HyperCard.  (See the examples below.)
  89.  
  90. dialogCount:
  91. If dialogCount is 2 (or anything other than 0 or 1), then the user will see both the Page Setup and the Print Job dialog boxes before the printing process begins.  If dialogCount is 1, then the user will see only the Print Job dialog box.  If dialogCount is 0, then neither of these dialog boxes will appear before printing.  If you set dialogCount equal to 0 or 1, thereby supressing one or both of the print dialogs, then the default settings stored in the printer resource file will be used for the print job, just as if the user clicked "OK" without changing any of the settings in the dialog.
  92.  
  93. There is no way in this version to set such things as paper orientation or number of copies to values other than the defaults by any means other than the dialog boxes.  Moreover, PrintField is unable to access the settings the user designates by choosing Page Setup from HyperCard's File menu.  If you want to enable printing with settings other than the defaults, set dialogCount to 2.
  94.  
  95. Setting Margins:
  96. Values for leftMargin, rightMargin, topMargin, and bottomMargin are given in pixels.  According to QuickDraw, one pixel equals 1/72 inch; therefore you can specify a one inch margin by passing 72 as the margin parameter.  If you don't supply margin parameters, PrintField defaults to half-inch margins on the top, left, and bottom of the page, and prints each line of the text about as wide as it appears on the screen.
  97.  
  98. Valid Examples--
  99. 1.  PrintField "bkgnd field 1"                -- default values will be used for margins
  100. 2.  PrintField "bkgnd field 1",0,0,0,0,0     -- this will print as much as possible on a page
  101. 3.  PrintField "card field id 22",2,72,72,72,72     -- by id, with one inch margins all around
  102. 4.  PrintField "card field foo",2,72          -- by name, with a left margin one inch wide
  103.  
  104. See the script of the button "Print the Documentation" for another example.
  105.  
  106. It is possible to put the designation of the field into a variable and then pass the variable to PrintField, as follows‚Ķ
  107.  
  108. on openField
  109.   if the optionKey is down then
  110.     put the name of the target into theName
  111.     put the id of the target into theField
  112.     if word 1 of theName is "card" then put "card field id " before theField
  113.     else put "field id " before theField
  114.     PrintField theField
  115.   end if
  116. end openField
  117.  
  118. This handler prints a field if the field is clicked while the option key is down. 
  119.  
  120. COPYING PRINTFIELD INTO YOUR STACKS
  121. Warning to ResEdit and ResCopy users:  PrintField requires a DLOG resource and a DITL resource as well as the XCMD resource--these resources are named "PrintField" and numbered 9140 (the same number as the PrintField XCMD) so you can find them easily.  If either the DITL or the DLOG resource is not present, PrintField will still print properly, and printing can still be cancelled by pressing command-period, but the user won't have the benefit of the information the dialog provides.
  122.  
  123. CHANGE HISTORY
  124. 3/7/88 1.0
  125. 3/17/88 1.1 -- Fixed erasure problem when printing on LaserWriter with a small lineHeight.
  126. 4/7/88 1.2 -- Dialog now centered on third party screens also.
  127. 6/8/88 1.4 -- Fixed problem printing multiple pages on ImageWriter.  Improved error handling to accord with changes in HyperCard 1.2.  Added option of skipping one or both print dialogs.  Improved WYSIWYG default margins (the width of the line on the page is much more often the same as it appears on the screen).  Fixed problem with last line of page when font ascent was small.
  128.  
  129. NOTES FOR PROGRAMMERS:
  130. PrintField contains code for finding out about all the properties of a field.  You might find the functions GetFontOfField, GetJustOfField, etc., to be useful in your XCMD's.
  131.  
  132. -- part contents for card part 12
  133. ----- text -----
  134. UNIT PrintUnit;
  135. { PrintField XCMD ¬©1988 by the Trustees of Dartmouth College. }
  136. { Written by John K. Calhoun, Courseware Development. }
  137.  
  138. INTERFACE
  139.  
  140.  USES
  141.   XCmdIntf, PrintTraps, ColorQuickDraw;
  142.  
  143.  CONST
  144.   idledlg = 9140;
  145.   marginDefault = 36;
  146.   throwAway = 5;
  147.   scrollWidth = 18;
  148.   wideExtra = 4;
  149.  
  150.  TYPE
  151.   Str31 = STRING[31];
  152.  
  153.  PROCEDURE Main (paramPtr : XCmdPtr);
  154.  
  155. IMPLEMENTATION
  156.  
  157. {----------------------------------------------------------------}
  158.  
  159. { The following routines are part of the XCMDUtilities.p file. }
  160. { I had been in the practice of including this file as part of my XCMD projects, }
  161. { but it contains some routines that I don't use (and pads the size of my }
  162. { resulting XCMD).  Now I just copy the routines I need directly into my code. }
  163.  
  164.  PROCEDURE DoJsr (addr : ProcPtr);
  165.  INLINE
  166.   $205F, $4E90;
  167.  
  168.  FUNCTION StringMatch (paramPtr : XCmdPtr;
  169.          pattern : Str255;
  170.          target : Ptr) : Ptr;
  171.  BEGIN
  172.   WITH paramPtr^ DO
  173.    BEGIN
  174.     inArgs[1] := ORD(@pattern);
  175.     inArgs[2] := ORD(target);
  176.     request := xreqStringMatch;
  177.     DoJsr(entryPoint);
  178.     StringMatch := Ptr(outArgs[1]);
  179.    END;
  180.  END;
  181.  
  182.  FUNCTION EvalExpr (paramPtr : XCmdPtr;
  183.          expr : Str255) : Handle;
  184.  BEGIN
  185.   WITH paramPtr^ DO
  186.    BEGIN
  187.     inArgs[1] := ORD(@expr);
  188.     request := xreqEvalExpr;
  189.     DoJsr(entryPoint);
  190.     EvalExpr := Handle(outArgs[1]);
  191.    END;
  192.  END;
  193.  
  194.  PROCEDURE ZeroToPas (paramPtr : XCmdPtr;
  195.          zeroStr : Ptr;
  196.          VAR pasStr : Str255);
  197.  BEGIN
  198.   WITH paramPtr^ DO
  199.    BEGIN
  200.     inArgs[1] := ORD(zeroStr);
  201.     inArgs[2] := ORD(@pasStr);
  202.     request := xreqZeroToPas;
  203.     DoJsr(entryPoint);
  204.    END;
  205.  END;
  206.  
  207.  FUNCTION StrToNum (paramPtr : XCmdPtr;
  208.          str : Str31) : LongInt;
  209.  BEGIN
  210.   WITH paramPtr^ DO
  211.    BEGIN
  212.     inArgs[1] := ORD(@str);
  213.     request := xreqStrToNum;
  214.     DoJsr(entryPoint);
  215.     StrToNum := outArgs[1];
  216.    END;
  217.  END;
  218.  
  219.  FUNCTION GetFieldByID (paramPtr : XCmdPtr;
  220.          cardFieldFlag : BOOLEAN;
  221.          fieldID : INTEGER) : Handle;
  222.  BEGIN
  223.   WITH paramPtr^ DO
  224.    BEGIN
  225.     inArgs[1] := ORD(cardFieldFlag);
  226.     inArgs[2] := fieldID;
  227.     request := xreqGetFieldByID;
  228.     DoJsr(entryPoint);
  229.     GetFieldByID := Handle(outArgs[1]);
  230.    END;
  231.  END;
  232.  
  233.  FUNCTION NumToStr (paramPtr : XCmdPtr;
  234.          num : LongInt) : Str31;
  235.   VAR
  236.    str : Str31;
  237.  BEGIN
  238.   WITH paramPtr^ DO
  239.    BEGIN
  240.     inArgs[1] := num;
  241.     inArgs[2] := ORD(@str);
  242.     request := xreqNumToStr;
  243.     DoJsr(entryPoint);
  244.     NumToStr := str;
  245.    END;
  246.  END;
  247.  
  248.  FUNCTION PasToZero (paramPtr : XCmdPtr;
  249.          str : Str255) : Handle;
  250.  BEGIN
  251.   WITH paramPtr^ DO
  252.    BEGIN
  253.     inArgs[1] := ORD(@str);
  254.     request := xreqPasToZero;
  255.     DoJsr(entryPoint);
  256.     PasToZero := Handle(outArgs[1]);
  257.    END;
  258.  END;
  259.  
  260.  FUNCTION StringLength (paramPtr : XCmdPtr;
  261.          strPtr : Ptr) : LongInt;
  262.  BEGIN
  263.   WITH paramPtr^ DO
  264.    BEGIN
  265.     inArgs[1] := ORD(strPtr);
  266.     request := xreqStringLength;
  267.     DoJsr(entryPoint);
  268.     StringLength := outArgs[1];
  269.    END;
  270.  END;
  271.  
  272.  PROCEDURE SendCardMessage (paramPtr : XCmdPtr;
  273.          msg : Str255);
  274.  BEGIN
  275.   WITH paramPtr^ DO
  276.    BEGIN
  277.     inArgs[1] := ORD(@msg);
  278.     request := xreqSendCardMessage;
  279.     DoJsr(entryPoint);
  280.    END;
  281.  END;
  282. {----------------------------------------------------------------}
  283.  
  284.  PROCEDURE myStdRect (verb : GrafVerb;
  285.          r : rect);
  286. { This procedure will replace the StdRect QuickDraw bottleneck procedure }
  287. { when we go into our printing loop.  We use TEUpdate to draw text, and }
  288. { TEUpdate calls EraseRect to clear the way before it draws anything. }
  289. { On a blank sheet of paper, there's no need to erase, so we replace the }
  290. { QuickDraw bottleneck that handles rectangles with a "do-nothing" procedure. }
  291. { This has two advantages when printing on the LaserWriter:  }
  292. {    1) Printing becomes faster, and  }
  293. {    2) A problem is avoided--the rectangles erased sometimes included the }
  294. { descenders of lines of text already drawn.  This happened with PrintField 1.0, }
  295. { which produced lines of text that were cut off at the bottom when the lineHeight }
  296. { of the field was smaller than about 4/3 of the average character height. }
  297.  BEGIN
  298.  END;
  299.  
  300.  FUNCTION GetScreenBitsBounds : Rect;
  301.   VAR
  302.    phonyPort : GrafPort;
  303.    savePort : GrafPtr;
  304. { This procedure tells us screenbits.bounds, which we can't get }
  305. { by conventional means, having no access to the QD globals. }
  306.  BEGIN
  307.   GetPort(savePort);
  308.   OpenPort(@phonyPort);
  309.   GetScreenBitsBounds := phonyPort.portRect;
  310. { When a port is opened, the initial setting of its portRect }
  311. { is screenbits.bounds (IM I-163) }
  312.   ClosePort(@phonyPort);
  313.   SetPort(savePort);
  314.  END;
  315.  
  316.  FUNCTION GetField (paramPtr : XCmdPtr;
  317.          whichField : Str255;
  318.          VAR theHandle : Handle) : OSErr;
  319. { Given a valid designation of a field--by name, id, or number, }
  320. { returns the text of the field in a handle. }
  321. { We go through all of this to make sure we're printing the contents of }
  322. { a real field, and not just of an ordinary container, because later we'll }
  323. { be using field properties--lineHeight and so on--in order to format our output. }
  324.   VAR
  325.    theString : Str255;
  326.    cardFieldFlag : BOOLEAN;
  327.    matchPtr : Ptr;
  328.    theResult : Handle;
  329.    fieldID : INTEGER;
  330.  BEGIN
  331.   theResult := EvalExpr(paramPtr, CONCAT('the long name of ', whichField));
  332.    { if HC can get the long name of the object designated by whichField, it's a valid object }
  333.   IF (theResult <> NIL) AND (paramPtr^.result = noErr) THEN
  334.    BEGIN
  335.     MoveHHi(theResult);
  336.     HLock(theResult);
  337.     matchPtr := StringMatch(paramPtr, 'field', theResult^);
  338.    { if the long name of the object contains the word "field," then it's a field }
  339.     IF matchPtr <> NIL THEN
  340.      BEGIN
  341.       matchPtr := StringMatch(paramPtr, 'card field', theResult^);
  342.    { we want to know if it's a card or background field in order }
  343.    { to set the cardFieldFlag for GetFieldbyID, below }
  344.       cardFieldFlag := (matchPtr <> NIL);
  345.       HUnlock(theResult);
  346.       DisposHandle(theResult);
  347.       theResult := EvalExpr(paramPtr, CONCAT('the id of ', whichField));
  348.    { we get the id of the field so that we can call GetFieldbyID }
  349. { Could use EvalExpr at this point, because we already know it's a field.}
  350.       IF (theResult <> NIL) AND (paramPtr^.result = noErr) THEN
  351.        BEGIN
  352.         MoveHHi(theResult);
  353.         HLock(theResult);
  354.         ZeroToPas(paramPtr, theResult^, theString);
  355.         fieldID := StrToNum(paramPtr, theString);
  356.         HUnlock(theResult);
  357.         DisposHandle(theResult);
  358.         theResult := GetFieldByID(paramPtr, cardFieldFlag, fieldID);  { get the text }
  359.        END;  { if theResult <> nil when asking for the id of the field }
  360.      END  { if "field" is part of the long name of the object }
  361.     ELSE
  362.      BEGIN
  363.       HUnlock(theResult);
  364.       DisposHandle(theResult);
  365.       theResult := NIL;
  366.      END;
  367.    END;  { if theResult <> nil when asking for the long name of the object }
  368.   theHandle := theResult;
  369.   GetField := paramPtr^.result;
  370.  END;  { function GetField }
  371.  
  372.  FUNCTION GetRectOfField (paramPtr : XCmdPtr;
  373.          whichField : Str255) : Rect;
  374.   VAR
  375.    theRect : rect;
  376.  
  377.   FUNCTION GetRectItem (theItem : INTEGER) : INTEGER;
  378.    VAR
  379.     theResult : Handle;
  380.     rectItemStr : Str255;
  381.   BEGIN
  382.    theResult := EvalExpr(paramPtr, CONCAT('item ', NumToStr(paramPtr, theItem), ' of the rect of ', whichField));
  383.    IF theResult <> NIL THEN
  384.     BEGIN
  385.      ZeroToPas(paramPtr, theResult^, rectItemStr);
  386.      DisposHandle(theResult);
  387.      GetRectItem := StrToNum(paramPtr, rectItemStr);
  388.     END
  389.    ELSE
  390.     GetRectItem := 0;
  391.   END;
  392.  
  393.  BEGIN
  394.   theRect.left := GetRectItem(1);
  395.   theRect.top := GetRectItem(2);
  396.   theRect.right := GetRectItem(3);
  397.   theRect.bottom := GetRectItem(4);
  398.  
  399.   GetRectOfField := theRect;
  400.  END;
  401.  
  402.  FUNCTION GetJustOfField (paramPtr : XCmdPtr;
  403.          whichField : Str255) : INTEGER;
  404.   VAR
  405.    theResult : Handle;
  406.    textAlign : INTEGER;
  407.    theStr : Str255;
  408.  BEGIN
  409.   theResult := EvalExpr(paramPtr, CONCAT('the textAlign of ', whichField));
  410.   ZeroToPas(paramPtr, theResult^, theStr);
  411.   DisposHandle(theResult);
  412.  
  413.   IF theStr = 'left' THEN
  414.    textAlign := teJustLeft
  415.   ELSE IF theStr = 'center' THEN
  416.    textAlign := teJustCenter
  417.   ELSE IF theStr = 'right' THEN
  418.    textAlign := teJustRight
  419.   ELSE
  420.    textAlign := teJustLeft;
  421.  
  422.   GetJustOfField := textAlign;
  423.  END;
  424.  
  425.  FUNCTION GetFontOfField (paramPtr : XCmdPtr;
  426.          whichField : Str255) : INTEGER;
  427.   VAR
  428.    theResult : Handle;
  429.    fontNumber : INTEGER;
  430.    fontName : Str255;
  431.  BEGIN
  432.   theResult := EvalExpr(paramPtr, CONCAT('the textFont of ', whichField));
  433.   ZeroToPas(paramPtr, theResult^, fontName);
  434.   DisposHandle(theResult);
  435.  
  436.   GetFNum(fontName, fontNumber);
  437.   GetFontOfField := fontNumber;
  438.  END;
  439.  
  440.  FUNCTION GetTextSizeOfField (paramPtr : XCmdPtr;
  441.          whichField : Str255) : INTEGER;
  442.   VAR
  443.    theResult : Handle;
  444.    theSize : INTEGER;
  445.    sizeStr : Str255;
  446.  BEGIN
  447.   theResult := EvalExpr(paramPtr, CONCAT('the textSize of ', whichField));
  448.   ZeroToPas(paramPtr, theResult^, sizeStr);
  449.   DisposHandle(theResult);
  450.  
  451.   theSize := StrToNum(paramPtr, sizeStr);
  452.   GetTextSizeOfField := theSize;
  453.  END;
  454.  
  455.  FUNCTION GetLineHeightOfField (paramPtr : XCmdPtr;
  456.          whichField : Str255) : INTEGER;
  457.   VAR
  458.    theResult : Handle;
  459.    theLineHeight : INTEGER;
  460.    heightStr : Str255;
  461.  BEGIN
  462.   theResult := EvalExpr(paramPtr, CONCAT('the textHeight of ', whichField));
  463.   ZeroToPas(paramPtr, theResult^, heightStr);
  464.   DisposHandle(theResult);
  465.  
  466.   theLineHeight := StrToNum(paramPtr, heightStr);
  467.   GetLineHeightOfField := theLineHeight;
  468.  END;
  469.  
  470.  PROCEDURE AdjustTextRect (paramPtr : XCmdPtr;
  471.          whichField : Str255;
  472.          VAR theRect : rect);
  473. { Given the rect of a field, adjusts the rectangle to make it the smallest }
  474. { rectangle containing the text of the field visible on the screen }
  475.   VAR
  476.    theResult : Handle;
  477.    theLineHeight : INTEGER;
  478.    theStr : Str255;
  479.  BEGIN
  480. { if the field has wide margins, we shrink the rectangle by wideExtra pixels all around }
  481.   theResult := EvalExpr(paramPtr, CONCAT('the wideMargins of ', whichField));
  482.   ZeroToPas(paramPtr, theResult^, theStr);
  483.   DisposHandle(theResult);
  484.   IF theStr = 'true' THEN
  485.    InsetRect(theRect, wideExtra, 0);
  486.  
  487. { if it's a scrolling field, we subtract scrollWidth from the right coordinate of the rect }
  488.   theResult := EvalExpr(paramPtr, CONCAT('the style of ', whichField));
  489.   ZeroToPas(paramPtr, theResult^, theStr);
  490.   DisposHandle(theResult);
  491.   IF theStr = 'scrolling' THEN
  492.    WITH theRect DO
  493.     right := right - scrollWidth;
  494.  END;
  495.  
  496.  FUNCTION GetTextStyleOfField (paramPtr : XCmdPtr;
  497.          whichField : Str255) : Style;
  498.   VAR
  499.    theTextStyle : Handle;
  500.    thePtr : Ptr;
  501.    theStyle : Style;
  502.  
  503.   FUNCTION StyleIs (textStyle : handle;
  504.           aStyle : Str255) : BOOLEAN;
  505.    VAR
  506.     thePtr : Ptr;
  507.   BEGIN
  508.    thePtr := StringMatch(paramPtr, aStyle, textStyle^);
  509.    IF thePtr <> NIL THEN
  510.     StyleIs := TRUE
  511.    ELSE
  512.     StyleIs := FALSE;
  513.   END;
  514.  
  515.  BEGIN
  516.   theStyle := [];
  517.  
  518.   theTextStyle := EvalExpr(paramPtr, CONCAT('the textStyle of ', whichField));
  519.   IF theTextStyle <> NIL THEN
  520.    BEGIN
  521.     IF StyleIs(theTextStyle, 'bold') THEN
  522.      theStyle := theStyle + [bold];
  523.  
  524.     IF StyleIs(theTextStyle, 'italic') THEN
  525.      theStyle := theStyle + [italic];
  526.  
  527.     IF StyleIs(theTextStyle, 'underline') THEN
  528.      theStyle := theStyle + [underline];
  529.  
  530.     IF StyleIs(theTextStyle, 'outline') THEN
  531.      theStyle := theStyle + [outline];
  532.  
  533.     IF StyleIs(theTextStyle, 'shadow') THEN
  534.      theStyle := theStyle + [shadow];
  535.  
  536.     IF StyleIs(theTextStyle, 'condense') THEN
  537.      theStyle := theStyle + [condense];
  538.  
  539.     IF StyleIs(theTextStyle, 'extend') THEN
  540.      theStyle := theStyle + [extend];
  541.  
  542.     DisposHandle(theTextStyle);
  543.    END;
  544.   GetTextStyleOfField := theStyle;
  545.  END;
  546.  
  547.  PROCEDURE PrintField (paramPtr : XCmdPtr);
  548.   VAR
  549.    currentPort : grafPtr;
  550.    myDlgPtr : DialogPtr;
  551.    myDITL, myDLOG : Handle;
  552.    dlogOK, goAhead : BOOLEAN;
  553.    prRecHdl : THPrint;
  554.    myStRec : TPrStatus;
  555.    myPrPort : TPPrPort;
  556.    theText : Handle;
  557.    hTE : TEHandle;
  558.    length : longint;
  559.    theTextStyle : Style;
  560.    theTextFont, theTextSize, theTextHeight, just : INTEGER;
  561.    FontIRec : FontInfo;
  562.    fieldName, str : Str255;
  563.    fieldRect, destRect, viewRect : rect;
  564.    fieldWidth, pageHeight, numLines, numCopies, myPgCount : INTEGER;
  565.    parameterCount, leftMargin, rightMargin, topMargin, bottomMargin : INTEGER;
  566.    ascentDiff : INTEGER;
  567.    dialogCount : LONGINT;
  568.    err : OSErr;
  569.    goToThisCard : Str255;
  570.  
  571.   PROCEDURE Fail (errMsg : Str255); { set theResult and quit }
  572.   BEGIN
  573.    paramPtr^.returnValue := PasToZero(paramPtr, errMsg);
  574.   END;
  575.  
  576.   PROCEDURE GetDialog; { check to see if DLOG and DITL are present; }
  577.                                          { if so, put up the dialog }
  578.   BEGIN
  579.    dlogOK := FALSE;
  580.    myDLOG := GetResource('DLOG', idledlg);
  581.    IF myDLOG <> NIL THEN
  582.     BEGIN
  583.      myDITL := GetResource('DITL', idledlg);
  584.      dlogOK := myDITL <> NIL;
  585.      IF dlogOK THEN
  586.       myDlgPtr := GetNewDialog(idledlg, NIL, NIL);
  587.     END;
  588.   END;
  589.  
  590.   PROCEDURE ShowDialog;
  591. { Here we mess with the appearance of the dialog.  We center it on the screen, }
  592. { and also center the text items within the dialog. }
  593. { Also, we grab the document name and make it the window title, so that the }
  594. { printer driver can access that information -- see Tech Note #72. }
  595.    VAR
  596.     mainScreenRect, dlogRect : rect;
  597.     hGlobal : INTEGER;
  598.     theDialogTHndl : DialogTHndl;
  599.     wMgrPort : GrafPtr;
  600.     wTitle : Str255;
  601.     theResult : Handle;
  602.  
  603.    PROCEDURE CenterTextItem (theDialogPtr : DialogPtr;
  604.            itemNo : INTEGER);
  605.     VAR
  606.      savePort : grafPtr;
  607.      item : handle;
  608.      itemType : INTEGER;
  609.      dlogRect, itemBox : Rect;
  610.      theStr : Str255;
  611.      width, textLeft : INTEGER;
  612.    BEGIN
  613.     GetDItem(theDialogPtr, itemNo, itemType, item, itemBox);
  614.     IF item <> NIL THEN
  615.      IF (itemType = statText) OR (itemType = statText + itemDisable) THEN
  616.       BEGIN
  617.        GetPort(savePort);
  618.        SetPort(theDialogPtr);
  619.        GetIText(item, theStr);
  620.        width := StringWidth(theStr) + 6;
  621.        IF itemNo = 1 THEN  { kludge for getting the width of an item that includes paramText }
  622.         width := width - StringWidth('^0') + StringWidth(wTitle);
  623.        dlogRect := theDialogPtr^.portRect;
  624.        WITH dlogRect DO
  625.         textLeft := (right - left - width) DIV 2;
  626.        WITH itemBox DO
  627.         BEGIN
  628.          left := textLeft;
  629.          right := textLeft + width;
  630.         END;
  631.        SetDItem(theDialogPtr, itemNo, itemType, item, itemBox);
  632.        SetPort(savePort);
  633.       END;
  634.    END;
  635.  
  636.   BEGIN
  637.    IF dlogOK THEN
  638.     BEGIN
  639.      theResult := EvalExpr(paramPtr, CONCAT('the short name of ', fieldName));
  640.      IF theResult <> NIL THEN
  641.       BEGIN
  642.        ZeroToPas(paramPtr, theResult^, wTitle);
  643.        DisposHandle(theResult);
  644.       END
  645.      ELSE
  646.       wTitle := fieldName;
  647.      SetWTitle(myDlgPtr, wTitle);
  648.      ParamText(wTitle, '', '', '');
  649.      CenterTextItem(myDlgPtr, 1);
  650.      CenterTextItem(myDlgPtr, 2);
  651.      myDLOG := GetResource('DLOG', idledlg);
  652.      theDialogTHndl := DialogTHndl(myDLOG);
  653. { we get the rect of the dialog directly from its DLOG resource in memory }
  654.      dlogRect := theDialogTHndl^^.boundsRect;
  655.      mainScreenRect := GetScreenBitsBounds;
  656.      WITH mainScreenRect DO
  657.       hGlobal := right - left;
  658.      WITH dlogRect DO
  659.       BEGIN
  660.        hGlobal := (hGlobal - (right - left)) DIV 2;
  661.        MoveWindow(myDlgPtr, hGlobal, top, FALSE);
  662.       END;
  663.      ShowWindow(myDlgPtr);
  664.      BringToFront(myDlgPtr);
  665.      DrawDialog(myDlgPtr);
  666.     END;
  667.   END;
  668.  
  669.   PROCEDURE SetDefaults;
  670.   BEGIN
  671.    viewRect := prRecHdl^^.prInfo.rPage;
  672.    topMargin := marginDefault;
  673.    bottomMargin := marginDefault;
  674.    leftMargin := marginDefault;
  675.    fieldRect := GetRectOfField(paramPtr, fieldName);
  676.    AdjustTextRect(paramPtr, fieldName, fieldRect);
  677.    WITH fieldRect DO
  678.     fieldWidth := right - left - throwAway;
  679.   END;
  680.  
  681.   PROCEDURE GetDialogCount;
  682.   BEGIN
  683.    IF parameterCount > 1 THEN
  684.     BEGIN
  685.      ZeroToPas(paramPtr, paramPtr^.params[2]^, str);
  686.      dialogCount := StrToNum(paramPtr, str);
  687.     END  { if parameterCount > 1 }
  688.    ELSE
  689.     dialogCount := 2;
  690.   END;
  691.  
  692.   PROCEDURE GetMarginSettings;
  693.    VAR
  694.     theBottom : INTEGER;
  695.   BEGIN
  696.    IF parameterCount > 2 THEN
  697.     BEGIN
  698.      ZeroToPas(paramPtr, paramPtr^.params[3]^, str);
  699.      leftMargin := StrToNum(paramPtr, str);
  700.      IF leftMargin < 0 THEN
  701.       leftMargin := marginDefault;
  702.      IF parameterCount > 3 THEN
  703.       BEGIN
  704.        ZeroToPas(paramPtr, paramPtr^.params[4]^, str);
  705.        rightMargin := StrToNum(paramPtr, str);
  706.        IF rightMargin >= 0 THEN
  707.         BEGIN
  708.          WITH viewRect DO
  709.           fieldWidth := right - left - leftMargin - rightMargin;
  710.          IF fieldWidth < 10 THEN
  711.           fieldWidth := 10;
  712.         END;
  713.        IF parameterCount > 4 THEN
  714.         BEGIN
  715.          ZeroToPas(paramPtr, paramPtr^.params[5]^, str);
  716.          topMargin := StrToNum(paramPtr, str);
  717.          IF topMargin < 0 THEN
  718.           topMargin := marginDefault;
  719.          IF parameterCount > 5 THEN
  720.           BEGIN
  721.            ZeroToPas(paramPtr, paramPtr^.params[6]^, str);
  722.            bottomMargin := StrToNum(paramPtr, str);
  723.            IF bottomMargin < 0 THEN
  724.             bottomMargin := marginDefault;
  725.           END;  { if parameterCount > 5 }
  726.         END;  { if parameterCount > 4 }
  727.       END;  { if parameterCount > 3 }
  728.     END;  { if parameterCount > 2 }
  729.    WITH viewRect DO
  730.     BEGIN
  731.      left := leftMargin;
  732.      top := topMargin;
  733.      right := leftMargin + fieldWidth;
  734.      bottom := bottom - bottomMargin;
  735.      IF bottom < top THEN
  736.       bottom := top + 10
  737.     END;
  738.   END;
  739.  
  740.   PROCEDURE GetFieldInfo;
  741.   BEGIN
  742.    just := GetJustOfField(paramPtr, fieldName);
  743.    theTextFont := GetFontOfField(paramPtr, fieldName);
  744.    theTextStyle := GetTextStyleOfField(paramPtr, fieldName);
  745.    theTextSize := GetTextSizeOfField(paramPtr, fieldName);
  746.    theTextHeight := GetLineHeightOfField(paramPtr, fieldName);
  747.   END;
  748.  
  749.   PROCEDURE SetPPortInfo;
  750.   BEGIN
  751.    TextFont(theTextFont);
  752.    TextFace(theTextStyle);
  753.    TextSize(theTextSize);
  754.    myPrPort^.gPort.txMode := srcOr;
  755.   END;
  756.  
  757.   PROCEDURE AdjustViewRect;
  758.   BEGIN
  759. { This will have to be modified if HyperCard ever uses the new Text Edit. }
  760.    WITH viewRect DO
  761.     pageheight := bottom - top;
  762.    numlines := pageheight DIV theTextHeight;
  763.    IF numlines < 1 THEN
  764.     numlines := 1;
  765.    pageheight := numlines * theTextHeight;
  766.    WITH viewRect DO
  767.     bottom := top + pageheight;
  768.    GetFontInfo(FontIRec);
  769.    destRect := viewRect;
  770.    destRect.bottom := 32767;
  771.   END;
  772.  
  773.   PROCEDURE GetPageCount;
  774. { This will have to be modified if HyperCard ever uses the new Text Edit. }
  775.   BEGIN
  776.    WITH hTE^^ DO
  777.     BEGIN
  778.      myPgCount := nLines DIV numlines;
  779.      IF (nLines MOD numlines) <> 0 THEN
  780.       myPgCount := myPgCount + 1;
  781.     END;
  782.   END;
  783.  
  784.   PROCEDURE SetTERec;
  785. { fill in the fields of the TextEdit Record }
  786.   BEGIN
  787.    MoveHHi(theText);
  788.    HLock(theText);
  789.    length := StringLength(paramPtr, theText^);
  790.    TESetText(theText^, length, hTE);
  791.    HPurge(theText);
  792.    HUnlock(theText);
  793.    DisposHandle(theText);
  794.    TESetJust(just, hTE);
  795.    WITH hTE^^ DO
  796.     BEGIN
  797.      txFont := theTextFont;
  798.      txFace := theTextStyle;
  799.      txSize := theTextSize;
  800.      fontAscent := theTextHeight - FontIRec.descent - FontIRec.leading;
  801.      lineHeight := theTextHeight;
  802.      crOnly := 1;
  803.      inPort := grafPtr(myPrPort);
  804.     END;
  805.   END;
  806.  
  807.   PROCEDURE PrintLoop;
  808.    VAR
  809.     copies, pg : INTEGER;
  810.   BEGIN
  811.    IF prRecHdl^^.prJob.bJDocLoop = bSpoolLoop THEN
  812.     numCopies := 1
  813.    ELSE
  814.     numCopies := prRecHdl^^.prJob.iCopies;
  815.    FOR copies := 1 TO numCopies DO
  816.     BEGIN
  817.      FOR pg := 1 TO myPgCount DO
  818.       BEGIN
  819.        PrOpenPage(myPrPort, NIL);
  820.        myPrPort^.gPort.grafProcs^.rectProc := @myStdRect;
  821. { It turned out that setting the bottlenecks any earlier didn't work -- }
  822. { PrOpenPage set them back to the postscript generating procedures. }
  823.        IF PrError = noErr THEN
  824.         TEUpdate(viewRect, hTE);
  825.        PrClosePage(myPrPort);
  826.        OffsetRect(hTE^^.destRect, 0, -pageHeight);
  827.       END;  { for pg := 1 to myPgCount }
  828.     END;  { for copies := 1 to numCopies }
  829.    PrCloseDoc(myPrPort);
  830.    IF (prRecHdl^^.prJob.bJDocLoop = bSpoolLoop) AND (PrError = noErr) THEN
  831.     PrPicFile(prRecHdl, NIL, NIL, NIL, myStRec);
  832.   END;
  833.  
  834.  BEGIN  { procedure PrintField }
  835.   InitCursor;
  836.   goToThisCard := 'go to this card';  { for updating HyperCard's window }
  837.   GetDialog;
  838.   parameterCount := paramPtr^.paramCount;
  839.   IF parameterCount > 0 THEN
  840.    BEGIN
  841.     ZeroToPas(paramPtr, paramPtr^.params[1]^, fieldName);
  842.     err := GetField(paramPtr, fieldName, theText);
  843.     IF (theText <> NIL) AND (err = noErr) THEN
  844.      BEGIN
  845.       HNoPurge(theText);
  846.       GetPort(currentPort);
  847.       PrOpen;
  848.       IF PrError = noErr THEN
  849.        BEGIN
  850.         prRecHdl := THPrint(NewHandle(SIZEOF(TPrint)));
  851.         IF (MemError = noErr) AND (prRecHdl <> NIL) THEN
  852.          BEGIN
  853.           PrintDefault(prRecHdl);
  854.           IF PrError = noErr THEN
  855.            BEGIN
  856.             GetDialogCount;
  857.             IF dialogCount IN [0, 1] THEN
  858.              goAhead := TRUE
  859.             ELSE
  860.              BEGIN
  861.               goAhead := PrStlDialog(prRecHdl);
  862.               SendCardMessage(paramPtr, goToThisCard);
  863.              END;
  864.             IF goAhead THEN
  865.              BEGIN
  866.               IF dialogCount = 0 THEN
  867.                goAhead := TRUE
  868.               ELSE
  869.                BEGIN
  870.                 goAhead := PrJobDialog(prRecHdl);
  871.                 SendCardMessage(paramPtr, goToThisCard);
  872.                END;
  873.               IF goAhead THEN
  874.                BEGIN
  875.                 ShowDialog;
  876.                 IF PrValidate(prRecHdl) THEN
  877.                  ; { we call PrValidate here only to give the printer }
  878.    { driver a chance to grab our document name -- see TN #72 }
  879.                 myPrPort := PrOpenDoc(prRecHdl, NIL, NIL);
  880.                 IF PrError = noErr THEN
  881.                  BEGIN
  882.                   SetDefaults;
  883.                   GetMarginSettings;
  884.                   GetFieldInfo;
  885.                   SetPPortInfo;
  886.                   AdjustViewRect;
  887.                   hTE := TENew(destRect, viewRect);
  888.                   IF hTE <> NIL THEN
  889.                    BEGIN
  890.                     SetTERec;
  891.                     GetPageCount;
  892.                     PrintLoop;
  893.                     TEDispose(hTE);
  894.                    END  { if we could create new TEHandle }
  895.                   ELSE
  896.                    BEGIN
  897.                     PrCloseDoc(myPrPort);
  898.                     IF MemError <> noErr THEN
  899.                      Fail(CONCAT('Error ', NumToStr(paramPtr, MemError)));
  900.                     IF GetHandleSize(theText) <> 0 THEN
  901.                      BEGIN
  902.                       HPurge(theText);
  903.                       HUnlock(theText);
  904.                       DisposHandle(theText);
  905.                      END;
  906.                    END;
  907.                  END
  908.                 ELSE
  909.                  PrCloseDoc(myPrPort);
  910.                END;  { if user confirms job dialog }
  911.              END;  { if user confirms style dialog }
  912.            END;  { if PrintDefault worked OK }
  913.           DisposHandle(Handle(prRecHdl));
  914.          END;  { if MemError = noErr when allocating print record }
  915.         PrClose;
  916.        END;  { if PrError = noErr when opening print driver }
  917.       IF PrError <> noErr THEN
  918.        Fail(CONCAT('Error ', NumToStr(paramPtr, PrError)))
  919.       ELSE IF MemError <> noErr THEN
  920.        Fail(CONCAT('Error ', NumToStr(paramPtr, MemError)));
  921.       SetPort(currentPort);
  922.      END  { if theText <> nil }
  923.     ELSE
  924.      Fail(CONCAT('Error -- never heard of ', fieldName));
  925.    END  { if we have at least 1 parameter }
  926.   ELSE
  927.    Fail('PrintField XCMD 1.4.1, 9 June 1988, ¬©1988 Dartmouth College');
  928.   IF dlogOK THEN
  929.    DisposDialog(myDlgPtr);
  930.  END;  { procedure PrintField }
  931.  
  932.  PROCEDURE Main;
  933.  BEGIN
  934.   PrintField(paramPtr);
  935.  END;
  936.  
  937. END.